package cn.iocoder.yudao.module.reading.service.auth;

import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.reading.controller.app.auth.vo.ReadingLoginReqVO;
import cn.iocoder.yudao.module.reading.controller.app.auth.vo.ReadingLoginRespVO;
import cn.iocoder.yudao.module.reading.controller.app.auth.vo.ReadingRegisterReqVO;
import cn.iocoder.yudao.module.reading.controller.app.auth.vo.ReadingWxLoginReqVO;
import cn.iocoder.yudao.module.reading.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.module.reading.dal.mysql.user.UserMapper;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collections;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.reading.enums.ErrorCodeConstants.*;

/**
 * 绘本阅读认证 Service 实现类
 *
 * @author 李晨
 */
@Service
@Validated
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private OAuth2TokenCommonApi oauth2TokenApi;

    @Resource
    private SocialUserApi socialUserApi;

    @Override
    public ReadingLoginRespVO login(@Valid ReadingLoginReqVO reqVO) {
        log.info("用户登录，用户名: {}", reqVO.getUsername());
        
        // 1. 校验用户名和密码
        UserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
        
        // 2. 创建访问令牌
        OAuth2AccessTokenRespDTO accessTokenRespDTO = createAccessToken(user.getId());
        
        // 3. 构建返回结果
        return buildLoginRespVO(user, accessTokenRespDTO);
    }

    @Override
    public ReadingLoginRespVO register(@Valid ReadingRegisterReqVO reqVO) {
        log.info("用户注册，用户名: {}", reqVO.getUsername()); // 注释掉手机号日志

        // 1. 校验用户注册信息（只检查未删除的用户）
        validateUserForRegister(reqVO);

        // 2. 创建新用户
        UserDO user = createUser(reqVO);

        // 3. 创建访问令牌
        OAuth2AccessTokenRespDTO accessTokenRespDTO = createAccessToken(user.getId());

        // 4. 构建返回结果
        return buildLoginRespVO(user, accessTokenRespDTO);
    }

    @Override
    public ReadingLoginRespVO wxLogin(@Valid ReadingWxLoginReqVO reqVO) {
        log.info("微信登录，code: {}", reqVO.getCode());

        try {
            // 1. 通过微信 code 获取社交用户信息
            SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(
                    UserTypeEnum.MEMBER.getValue(),
                    SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(),
                    reqVO.getCode(),
                    "default" // state参数，微信小程序可以使用默认值
            );

            if (socialUser == null) {
                log.error("微信登录失败：无法获取社交用户信息，code: {}", reqVO.getCode());
                throw exception(WECHAT_AUTH_FAILED);
            }

            UserDO user;
            String openid = socialUser.getOpenid();

            // 2. 查找或创建用户
            if (socialUser.getUserId() != null) {
                // 情况一：已绑定用户，获取用户信息并更新微信信息
                user = userMapper.selectById(socialUser.getUserId());
                if (user == null) {
                    log.error("微信登录失败：绑定的用户不存在，userId: {}", socialUser.getUserId());
                    throw exception(USER_NOT_EXISTS);
                }

                // 更新用户的微信信息（昵称和头像）
                updateUserWxInfo(user, socialUser, reqVO);

                log.info("微信登录：已绑定用户，userId: {}, openid: {}", user.getId(), openid);
            } else {
                // 情况二：未绑定用户，创建新用户并绑定
                user = createUserFromWxInfo(socialUser, reqVO);

                // 绑定社交用户
                socialUserApi.bindSocialUser(new SocialUserBindReqDTO(
                        user.getId(),
                        UserTypeEnum.MEMBER.getValue(),
                        SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(),
                        reqVO.getCode(),
                        "default"
                ));

                log.info("微信登录：创建新用户并绑定，userId: {}, openid: {}", user.getId(), openid);
            }

            // 3. 创建访问令牌
            OAuth2AccessTokenRespDTO accessTokenRespDTO = createAccessToken(user.getId());

            // 4. 构建返回结果
            ReadingLoginRespVO result = buildLoginRespVO(user, accessTokenRespDTO, openid);

            log.info("微信登录成功，userId: {}, openid: {}", user.getId(), openid);
            return result;

        } catch (Exception e) {
            log.error("微信登录异常，code: {}", reqVO.getCode(), e);
            if (e instanceof RuntimeException) {
                throw e;
            }
            throw exception(WECHAT_AUTH_FAILED);
        }
    }

    @Override
    public void logout(String token) {
        log.info("用户退出登录，token: {}", token);
        
        // 删除访问令牌
        oauth2TokenApi.removeAccessToken(token);
    }

    /**
     * 用户认证
     *
     * @param username 用户名或手机号
     * @param password 密码
     * @return 用户信息
     */
    private UserDO authenticate(String username, String password) {
        // 1. 查找用户
        UserDO user = userMapper.selectByUsernameOrPhone(username);
        if (user == null) {
            log.warn("用户不存在，用户名: {}", username);
            throw exception(USER_NOT_EXISTS);
        }
        
        // 2. 校验密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            log.warn("密码错误，用户ID: {}", user.getId());
            throw exception(USER_PASSWORD_INCORRECT);
        }
        
        log.info("用户认证成功，用户ID: {}", user.getId());
        return user;
    }

    /**
     * 校验用户注册信息
     *
     * @param reqVO 注册请求
     */
    private void validateUserForRegister(ReadingRegisterReqVO reqVO) {
        // 校验密码确认
        if (!reqVO.getPassword().equals(reqVO.getConfirmPassword())) {
            throw exception(USER_PASSWORD_NOT_MATCH);
        }

        // 校验用户名是否已存在（只检查未删除的用户）
        if (userMapper.selectByUsernameIncludeDeleted(reqVO.getUsername()) != null) {
            throw exception(USER_USERNAME_EXISTS);
        }

        // 注释掉手机号校验
        // if (userMapper.selectByPhoneIncludeDeleted(reqVO.getPhone()) != null) {
        //     throw exception(USER_PHONE_EXISTS);
        // }
    }

    /**
     * 创建用户
     *
     * @param reqVO 注册请求
     * @return 用户信息
     */
    private UserDO createUser(ReadingRegisterReqVO reqVO) {
        UserDO user = UserDO.builder()
                .username(reqVO.getUsername())
                .password(passwordEncoder.encode(reqVO.getPassword()))
                .nickname(reqVO.getUsername()) // 默认使用用户名作为昵称
                // .phone(reqVO.getPhone()) // 注释掉手机号设置
                .build();

        userMapper.insert(user);
        log.info("用户创建成功，用户ID: {}", user.getId());
        return user;
    }

    /**
     * 创建访问令牌
     *
     * @param userId 用户ID
     * @return 访问令牌信息
     */
    private OAuth2AccessTokenRespDTO createAccessToken(Long userId) {
        OAuth2AccessTokenCreateReqDTO createReqDTO = new OAuth2AccessTokenCreateReqDTO()
                .setUserId(userId)
                .setUserType(UserTypeEnum.MEMBER.getValue())
                .setClientId("reading-app") // 绘本阅读应用客户端ID
                .setScopes(Collections.singletonList("read"));
        
        return oauth2TokenApi.createAccessToken(createReqDTO);
    }

    /**
     * 更新用户的微信信息
     *
     * @param user 用户实体
     * @param socialUser 社交用户信息
     * @param reqVO 微信登录请求
     */
    private void updateUserWxInfo(UserDO user, SocialUserRespDTO socialUser, ReadingWxLoginReqVO reqVO) {
        boolean needUpdate = false;

        // 更新昵称（优先使用前端传递的用户信息）
        String newNickname = null;
        if (reqVO.getUserInfo() != null && reqVO.getUserInfo().getNickName() != null) {
            newNickname = reqVO.getUserInfo().getNickName();
        } else if (socialUser.getNickname() != null) {
            newNickname = socialUser.getNickname();
        }

        if (newNickname != null && !newNickname.equals(user.getNickname())) {
            String oldNickname = user.getNickname();
            user.setNickname(newNickname);
            needUpdate = true;
            log.info("更新用户昵称：userId={}, oldNickname={}, newNickname={}",
                    user.getId(), oldNickname, newNickname);
        }

        // 更新头像（优先使用前端传递的用户信息）
        String newAvatar = null;
        if (reqVO.getUserInfo() != null && reqVO.getUserInfo().getAvatarUrl() != null) {
            newAvatar = reqVO.getUserInfo().getAvatarUrl();
        } else if (socialUser.getAvatar() != null) {
            newAvatar = socialUser.getAvatar();
        }

        if (newAvatar != null && !newAvatar.equals(user.getAvatar())) {
            user.setAvatar(newAvatar);
            needUpdate = true;
            log.info("更新用户头像：userId={}, newAvatar={}", user.getId(), newAvatar);
        }

        // 如果有更新，保存到数据库
        if (needUpdate) {
            user.setUpdateTime(LocalDateTime.now());
            userMapper.updateById(user);
            log.info("用户微信信息更新成功：userId={}", user.getId());
        }
    }

    /**
     * 从微信信息创建用户
     *
     * @param socialUser 社交用户信息
     * @param reqVO 微信登录请求
     * @return 创建的用户
     */
    private UserDO createUserFromWxInfo(SocialUserRespDTO socialUser, ReadingWxLoginReqVO reqVO) {
        UserDO user = new UserDO();

        // 设置基本信息
        user.setUsername("wx_" + socialUser.getOpenid().substring(0, Math.min(socialUser.getOpenid().length(), 16)));
        user.setNickname(socialUser.getNickname() != null ? socialUser.getNickname() : "微信用户");
        user.setAvatar(socialUser.getAvatar());

        // 如果请求中包含用户信息，优先使用
        if (reqVO.getUserInfo() != null) {
            ReadingWxLoginReqVO.WxUserInfo wxUserInfo = reqVO.getUserInfo();
            if (wxUserInfo.getNickName() != null) {
                user.setNickname(wxUserInfo.getNickName());
            }
            if (wxUserInfo.getAvatarUrl() != null) {
                user.setAvatar(wxUserInfo.getAvatarUrl());
            }
        }

        // 设置创建时间
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        // 保存用户
        userMapper.insert(user);

        log.info("创建微信用户成功，userId: {}, username: {}, nickname: {}",
                user.getId(), user.getUsername(), user.getNickname());

        return user;
    }

    /**
     * 构建登录响应
     *
     * @param user 用户信息
     * @param accessTokenRespDTO 访问令牌信息
     * @return 登录响应
     */
    private ReadingLoginRespVO buildLoginRespVO(UserDO user, OAuth2AccessTokenRespDTO accessTokenRespDTO) {
        return buildLoginRespVO(user, accessTokenRespDTO, null);
    }

    /**
     * 构建登录响应（支持微信登录）
     *
     * @param user 用户信息
     * @param accessTokenRespDTO 访问令牌信息
     * @param openid 微信openid（可选）
     * @return 登录响应
     */
    private ReadingLoginRespVO buildLoginRespVO(UserDO user, OAuth2AccessTokenRespDTO accessTokenRespDTO, String openid) {
        ReadingLoginRespVO.UserInfo userInfo = ReadingLoginRespVO.UserInfo.builder()
                .id(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                // .phone(user.getPhone()) // 注释掉手机号返回
                .email(user.getEmail())
                .build();

        return ReadingLoginRespVO.builder()
                .userId(user.getId())
                .accessToken(accessTokenRespDTO.getAccessToken())
                .refreshToken(accessTokenRespDTO.getRefreshToken())
                .expiresTime(accessTokenRespDTO.getExpiresTime())
                .userInfo(userInfo)
                .openid(openid)
                .build();
    }
}
